package org.kamionowski.supreme.persistence.dao;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kamionowski.supreme.persistence.dao.beans.FileSearchBean;
import org.kamionowski.supreme.persistence.entity.FileDsc;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;

import javax.persistence.Query;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by IntelliJ IDEA.
 * User: Marcin Kamionowski
 * Date: 13.04.11
 * Time: 21:34
 */
@Repository
public class FileDscDAOJPA extends AbstractDAOJPA implements FileDscDAO{

    private final static Log log = LogFactory.getLog(FileDscDAOJPA.class);

    @Override
    public List<FileDsc> searchFiles(FileSearchBean searchFiles) {
        StringBuilder sql = new StringBuilder();

        sql.append("SELECT f FROM file_dsc f WHERE 1=1 ");
        List<QueryPart> sqlParts = new LinkedList<QueryPart>();

        if(!CollectionUtils.isEmpty(searchFiles.getExtensions())){
            sqlParts.add(new QueryPart(" AND f.ext IN (:ext) ", searchFiles.getExtensions()));
        }

        if(searchFiles.getModificationFrom() != null){
            sqlParts.add(new QueryPart(" AND f.date > :dateFrom ", searchFiles.getModificationFrom()));
        }

        if(searchFiles.getModificationTo() != null){
            sqlParts.add(new QueryPart(" AND f.date > :dateTo ", searchFiles.getModificationTo()));
        }

        if(!CollectionUtils.isEmpty(searchFiles.getTokens())){
            String likeStmt = Boolean.TRUE.equals(searchFiles.getCaseMatch()) ? "LIKE" : "ILIKE";
            int tokenCounter = 0;
            for(String token: searchFiles.getTokens()){
                sqlParts.add(new QueryPart(" AND f.dir || '/' || f.name " + likeStmt + " :token" + tokenCounter, "%" + token + "%"));
            }
        }

        Query query = prepareQuery(sql, sqlParts);
        return query.getResultList();
    }

    private Query prepareQuery(StringBuilder sql, List<QueryPart> sqlParts){
        for(QueryPart part : sqlParts){
            sql.append(part);
        }
        Query query = em.createNativeQuery(sql.toString());
        for(QueryPart part : sqlParts){
            part.setParam(query);
        }
        return query;
    }

    protected static class QueryPart{
        private final String sqlPart;
        private final Object paramValue;
        private final String paramName;
        private final Pattern paramPattern = Pattern.compile(".*:(\\w+).*");

        public QueryPart(String sqlPart, Object paramValue) {
            Validate.notNull(sqlPart);
            Validate.isTrue(sqlPart.contains(":"));
            Matcher m = paramPattern.matcher(sqlPart);
            Validate.isTrue(m.matches());

            this.sqlPart = sqlPart;
            this.paramValue = paramValue;
            this.paramName = m.group(1);
            log.debug("Bind param name \"" + this.paramName + "\" to sql part \"" + sqlPart + "\"");
        }

        public void setParam(Query q){
            q.setParameter(paramName, paramValue);
        }

        @Override
        public String toString() {
            return sqlPart;
        }
    }
}
